﻿#include "modbussensor.h"
#include <QDebug>
#include <QThread>

ModbusSensor::ModbusSensor(QObject *parent) : QObject(parent)
{
    serialPort = new QSerialPort(this);
}

ModbusSensor::~ModbusSensor()
{
    close();
    delete serialPort;
}

bool ModbusSensor::open(const QString &portName)
{
    serialPort->setPortName(portName);
    serialPort->setBaudRate(QSerialPort::Baud9600);
    serialPort->setDataBits(QSerialPort::Data8);
    serialPort->setParity(QSerialPort::NoParity);
    serialPort->setStopBits(QSerialPort::OneStop);
    serialPort->setFlowControl(QSerialPort::NoFlowControl);

    if (!serialPort->open(QIODevice::ReadWrite)) {
        qWarning() << "Failed to open port" << portName << ":" << serialPort->errorString();
        return false;
    }
    return true;
}

void ModbusSensor::close()
{
    if (serialPort->isOpen()) {
        serialPort->close();
    }
}

// MODBUS CRC16 计算
quint16 ModbusSensor::calculateCRC(const QByteArray &data)
{
    quint16 crc = 0xFFFF;
    for (int pos = 0; pos < data.size(); pos++) {
        crc ^= (quint8)data[pos];
        for (int i = 8; i != 0; i--) {
            if ((crc & 0x0001) != 0) {
                crc >>= 1;
                crc ^= 0xA001;
            } else {
                crc >>= 1;
            }
        }
    }
    return crc;
}

// 发送命令并接收响应
QByteArray ModbusSensor::sendCommand(const QByteArray &command, int expectedLength, int timeout)
{
    if (!serialPort->isOpen()) {
        qWarning() << "Serial port is not open";
        return QByteArray();
    }

    // 计算CRC并附加到命令
    quint16 crc = calculateCRC(command);
    QByteArray fullCommand = command;
    fullCommand.append(static_cast<char>(crc & 0xFF)); // 低字节在前
    fullCommand.append(static_cast<char>((crc >> 8) & 0xFF));

    // 发送命令
    serialPort->write(fullCommand);
    if (!serialPort->waitForBytesWritten(timeout)) {
        qWarning() << "Write operation timed out";
        return QByteArray();
    }

    // 等待响应
    QByteArray response;
    while (response.size() < expectedLength && serialPort->waitForReadyRead(timeout)) {
        response += serialPort->readAll();
    }

    // 验证响应长度
    if (response.size() < 4) { // 最小响应长度（地址+功能码+CRC）
        qWarning() << "Response too short:" << response.toHex();
        return QByteArray();
    }

    // 验证CRC
    QByteArray responseWithoutCRC = response.left(response.size() - 2);
    quint16 receivedCRC = (static_cast<quint8>(response.at(response.size()-1) << 8) |
                          static_cast<quint8>(response.at(response.size()-2)));

    if (calculateCRC(responseWithoutCRC) != receivedCRC) {
        qWarning() << "CRC mismatch";
        return QByteArray();
    }

    return responseWithoutCRC;
}

// 读取所有传感器数据
QMap<QString, double> ModbusSensor::readAllSensors(quint8 address)
{
    QMap<QString, double> result;

    // 构建命令：地址 + 功能码03 + 起始地址00 01 + 寄存器数量00 0B
    QByteArray command;
    command.append(static_cast<char>(address));
    command.append(0x03);
    command.append(static_cast<char>(0x00));
    command.append(static_cast<char>(0x01)); // 起始地址0x0001
    command.append(static_cast<char>(0x00));
    command.append(static_cast<char>(0x0B)); // 11个寄存器

    QByteArray response = sendCommand(command, 25); // 期望响应长度：25字节
    if (response.size() != 23) { // 1(地址)+1(功能码)+1(字节数)+22(数据)=25字节，去掉CRC2，所以是23字节
        qWarning() << "Invalid response length:" << response.size() << "expected 23";
        return result;
    }

    // 解析数据
    for (int i = 0; i < 11; i++) {
        int pos = 3 + i * 2;
        quint16 value = (static_cast<quint8>(response.at(pos)) << 8 |
                        static_cast<quint8>(response.at(pos+1)));

        double convertedValue = 0.0;

        switch (i) {
        case 0: // 温度
            convertedValue = value / 100.0 - 40;
            result["temperature"] = convertedValue;
            break;
        case 1: // 湿度
            convertedValue = value / 100.0;
            result["humidity"] = convertedValue;
            break;
        case 2: // 气压
            convertedValue = value / 10.0;
            result["pressure"] = convertedValue;
            break;
        case 3: // 风速
            convertedValue = value / 100.0;
            result["wind_speed"] = convertedValue;
            break;
        case 4: // 风向
            convertedValue = value / 10.0;
            result["wind_direction"] = convertedValue;
            break;
        case 5: // 雨量
            convertedValue = value / 10.0;
            result["rainfall"] = convertedValue;
            break;
        case 6: // 辐射
            convertedValue = value;
            result["radiation"] = convertedValue;
            break;
        case 7: // 光照
            convertedValue = value / 100.0;
            result["illuminance"] = convertedValue;
            break;
        case 8: // 日照时数
            convertedValue = value / 10.0;
            result["sunshine_hours"] = convertedValue;
            break;
        case 9: // 露点温度
            convertedValue = value / 10.0;
            result["dew_point"] = convertedValue;
            break;
        case 10: // ET0
            convertedValue = value / 10.0;
            result["et0"] = convertedValue;
            break;
        }
    }

    return result;
}

// 读取设备地址
quint8 ModbusSensor::readDeviceAddress()
{
    QByteArray command;
    command.append(static_cast<char>(0x00)); // 广播地址
    command.append(0x03); // 功能码
    command.append(static_cast<char>(0x00)); // 寄存器地址高字节
    command.append(static_cast<char>(0x00)); // 寄存器地址低字节
    command.append(static_cast<char>(0x00)); // 寄存器数量高字节
    command.append(static_cast<char>(0x01)); // 寄存器数量低字节

    QByteArray response = sendCommand(command, 7); // 期望响应长度：7字节
    if (response.size() != 5) { // 1(地址)+1(功能码)+1(字节数)+2(数据)=5字节
        qWarning() << "Invalid response length for address read:" << response.size();
        return 0xFF; // 返回默认地址表示失败
    }

    // 解析地址
    quint16 address = (static_cast<quint8>(response.at(3)) << 8 |
                      static_cast<quint8>(response.at(4)));
    return static_cast<quint8>(address);
}

// 修改设备地址
bool ModbusSensor::changeDeviceAddress(quint8 newAddress)
{
    QByteArray command;
    command.append(static_cast<char>(0x00)); // 广播地址
    command.append(0x06); // 功能码
    command.append(static_cast<char>(0x00)); // 寄存器地址高字节
    command.append(static_cast<char>(0x00)); // 寄存器地址低字节
    command.append(static_cast<char>(0x00)); // 新地址高字节
    command.append(static_cast<char>(newAddress)); // 新地址低字节

    QByteArray response = sendCommand(command, 8); // 期望响应长度：8字节
    if (response.size() != 6) { // 1(地址)+1(功能码)+2(寄存器地址)+2(数据)=6字节
        qWarning() << "Invalid response length for address change:" << response.size();
        return false;
    }

    // 验证响应是否与发送的命令相同
    return response == command;
}
